/**************************************************************************(C)*
 *
 *  This file is part of the GreenHills project
 *  Copyright (C) 2009  Tieto Corporation
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Not for commmercial use. Only with Tieto permission.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************(C)*/

#include "server.h"
#include "client.h"

#include "../net/msgserversocketconnection.h"

#include "../event/additemevent.h"
#include "../event/removeitemevent.h"
#include "../event/updateobjectevent.h"

#include "../io/connection.h"
#include "../util/binstream.h"
#include "../util/memstreambuf.h"
#include "../util/conversion.h"
#include "../core/user.h"

#include <assert.h>

using namespace greenhills::event;
using namespace greenhills::server;
using namespace greenhills::net;
using namespace greenhills::io;
using namespace greenhills::util;


CServer::CServer()
{
    mDefaultRoom = new server::CRoom(
            DEFAULT_ROOM_ID, DEFAULT_ROOM_NAME, DEFAULT_PASSWORD);
    mDefaultRoom->registerListener(this);
}

CServer::~CServer()
{
    mDefaultRoom->unregisterListener(this);
    delete mDefaultRoom;
}

greenhills::server::CRoom *CServer::room(
        const greenhills::core::CRoomId &roomId)
{
    if (roomId == DEFAULT_ROOM_ID)
        return mDefaultRoom;

    return 0;
}

greenhills::core::CClientId CServer::login(const std::string &pUsername,
        const std::string &pPassword)
{
    Q_UNUSED(pUsername);
    if (pPassword=="")
    {
        CClient *client =
            new CClient(pUsername);
        mClients.add(client);

        std::cout << "Login Succeeded - Username: " << pUsername << std::endl;
        return client->id();
    }
    else
    {
        std::cout << "Login Failed - Username: " << pUsername << std::endl;
        return 0;
    }
}

bool CServer::logout(const greenhills::core::CClientId &pClientId)
{
    return clients().remove(pClientId);
}

greenhills::core::CRoomId CServer::createRoom(
        const greenhills::core::CClientId &pClientId,
        const std::string &pRoomName,
        const greenhills::core::CRoomType &pRoomType,
        const std::string &pPassword)
{
    Q_UNUSED(pClientId);
    Q_UNUSED(pRoomName);
    Q_UNUSED(pRoomType);
    Q_UNUSED(pPassword);
    return DEFAULT_ROOM_ID;
}

bool CServer::joinRoom(const greenhills::core::CClientId &pClientId,
        const greenhills::core::CRoomId &pRoomId, const std::string &pPassword)
{
    server::CRoom *roomToJoin = room(pRoomId);
    if (roomToJoin == 0)
    {
        std::cout << "Join Room Failed - invalid room Id" << std::endl;
        return false;
    }

    if (!roomToJoin->isPasswordValid(pPassword))
    {
        std::cout << "Join Room Failed - invalid password" << std::endl;
        return false;
    }

    std::cout << "Join Room Succeeded" << std::endl;

    CClient *client = clients().client(pClientId);
    assert(client != 0);

    for(CRoom::ItemIterator it = roomToJoin->begin();
            it != roomToJoin->end(); it++)
    {
        const greenhills::core::IRoomItem *roomItem = it->second;

        CItemAddedEvent itemAddedEvent(pRoomId, roomItem->id(),
                roomItem->name(), roomItem->type());
        client->sendEvent(&itemAddedEvent);
    }

    roomToJoin->joinClient(pClientId);
    return true;
}

bool CServer::leaveRoom(const greenhills::core::CClientId &pClientId,
        const greenhills::core::CRoomId &pRoomId)
{
    server::CRoom *roomToLeave = room(pRoomId);
    if (roomToLeave == 0)
    {
        std::cout << "Leave Room Failed" << std::endl;
        return false;
    }

    std::cout << "Leave Room Succeeded" << std::endl;
    roomToLeave->removeClient(pClientId);

    return true;
}

greenhills::core::CItemId CServer::addItem(
        const greenhills::core::CClientId &pClientId,
        const greenhills::core::CRoomId &pRoomId, const std::string &pItemName,
        const greenhills::core::CItemType &pItemType)
{
    Q_UNUSED(pClientId);

    server::CRoom *roomToAdd = room(pRoomId);
    if (roomToAdd == 0)
    {
        std::cout << "Add Item Failed" << std::endl;
        return false;
    }

    greenhills::core::CItemId newItemId = getUniqueItemId();
    if (!mDefaultRoom->addItem( newItemId, pItemName, pItemType ))
    {
        std::cout << "Add Item Failed" << std::endl;
        return false;
    }

    std::cout << "Add Item Suceeded" << std::endl;
    return newItemId;
}

bool CServer::removeItem(const greenhills::core::CClientId &pClientId,
        const greenhills::core::CRoomId &pRoomId,
        const greenhills::core::CItemId &pItemId)
{
    Q_UNUSED(pClientId);

    server::CRoom *roomToRemove = room(pRoomId);
    roomToRemove->removeItem(pItemId);

    return true;
}

bool CServer::updateItem(const greenhills::core::CClientId &pClientId,
        const greenhills::core::CRoomId &pRoomId,
        const greenhills::core::CItemId &pItemId,
        const unsigned char *pData, unsigned int pLength)
{
    Q_UNUSED(pClientId);

    if ((int)pRoomId == (int)mDefaultRoom->id())
    {
        mDefaultRoom->updateItem(pItemId, pData, pLength);

        greenhills::core::IRoomItem *roomItem = mDefaultRoom->item(pItemId);
        assert(roomItem != 0);

        roomItem->updateItem(pData, pLength);

//        std::cout << "Room Object Updated" << std::endl;
//        std::cout << "   RoomId: " << pRoomId << std::endl;
//        std::cout << "   ObjectId: " << roomItem->id() << std::endl;
//
//        CPositionedRoomItem *positionedRoomItem =
//                dynamic_cast<CPositionedRoomItem*>(roomItem);
//
//        if (positionedRoomItem != 0)
//        {
//            std::cout << "   TimeStamp: "
//                    << sec2str(positionedRoomItem->timeStamp()) << std::endl;
//            std::cout << "   Position: "
//                    << qstr2std(positionedRoomItem->position().toString())
//                    << std::endl;
//            std::cout << "   Altitude: "
//                    << positionedRoomItem->altitude() << std::endl;
//            std::cout << "   Speed: "
//                    << positionedRoomItem->movementSpeed() << std::endl;
//            std::cout << "   Direction: "
//                    << positionedRoomItem->movementDirection() << std::endl;
//        }
    }

    return true;
}

greenhills::core::CItemId CServer::getUniqueItemId()
{
    static greenhills::core::CItemId globalItemId = 0;
    return ++globalItemId;
}

void CServer::aboutToAddItem(greenhills::core::IRoom *pRoom,
    const greenhills::core::CItemId &pItemId)
{
    Q_UNUSED(pRoom);
    Q_UNUSED(pItemId);
}

void CServer::aboutToUpdateItem(greenhills::core::IRoom *pRoom,
    const greenhills::core::CItemId &pItemId)
{
    Q_UNUSED(pRoom);
    Q_UNUSED(pItemId);
}

void CServer::aboutToRemoveItem(greenhills::core::IRoom *pRoom,
    const greenhills::core::CItemId &pItemId)
{
    Q_UNUSED(pRoom);
    Q_UNUSED(pItemId);
}

void CServer::itemAdded(greenhills::core::IRoom *pRoom,
        const greenhills::core::CItemId &pItemId, const std::string &pItemName,
        const greenhills::core::CItemType &pItemType)
{
    std::cout << "IRoomListener::itemAdded" << std::endl;
    std::cout << "   RoomId: " << pRoom->id() << std::endl;
    std::cout << "   ItemId: " << pItemId << std::endl;
    std::cout << "   ItemName: " << pItemName << std::endl;
    std::cout << "   ItemType: " << pItemType << std::endl;

    CItemAddedEvent event(pRoom->id(), pItemId, pItemName, pItemType);

    server::CRoom *affectedRoom = static_cast<server::CRoom*>(pRoom);
    for(server::CRoom::ClientsIterator it = affectedRoom->clientsBegin();
            it != affectedRoom->clientsEnd(); it++)
    {
        CClient * client = clients().client(*it);
        client->sendEvent(&event);
    }
}

void CServer::itemUpdated(greenhills::core::IRoom *pRoom,
        const greenhills::core::CItemId &pItemId, const unsigned char *pData,
        unsigned int pLength)
{
    (void) pData;

//    std::cout << "IRoomListener::itemUpdated" << std::endl;
//    std::cout << "   RoomId: " << pRoom->id() << std::endl;
//    std::cout << "   ItemId: " << pItemId << std::endl;
//    std::cout << "   DataLength: " << pLength << std::endl;

    CItemUpdatedEvent event(pRoom->id(), pItemId, pData, pLength);

    server::CRoom *affectedRoom = static_cast<server::CRoom*>(pRoom);
    for(server::CRoom::ClientsIterator it = affectedRoom->clientsBegin();
            it != affectedRoom->clientsEnd(); it++)
    {
        CClient * client = clients().client(*it);
        client->sendEvent(&event);
    }
}

void CServer::itemRemoved(greenhills::core::IRoom *pRoom,
        const greenhills::core::CItemId &pItemId)
{
    std::cout << "IRoomListener::itemRemoved" << std::endl;
    std::cout << "   RoomId: " << pRoom->id() << std::endl;
    std::cout << "   ItemId: " << pItemId << std::endl;

    CItemRemovedEvent event(pRoom->id(), pItemId);

    server::CRoom *affectedRoom = static_cast<server::CRoom*>(pRoom);
    for(server::CRoom::ClientsIterator it = affectedRoom->clientsBegin();
            it != affectedRoom->clientsEnd(); it++)
    {
        CClient * client = clients().client(*it);
        client->sendEvent(&event);
    }
}

